<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Spring MVC中实现RESTful API指南</title>
    <link href="https://cdn.staticfile.org/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
    <link href="https://cdn.staticfile.org/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
    <link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;500;600;700&family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet">
    <style>
        body {
            font-family: 'Noto Sans SC', Tahoma, Arial, Roboto, "Droid Sans", "Helvetica Neue", "Droid Sans Fallback", "Heiti SC", "Hiragino Sans GB", Simsun, sans-serif;
            line-height: 1.8;
            color: #333;
        }
        
        .hero {
            background: linear-gradient(135deg, #6e48aa 0%, #9d50bb 100%);
        }
        
        .code-block {
            background: #2d2d2d;
            border-radius: 8px;
            box-shadow: 0 10px 30px rgba(0,0,0,0.3);
        }
        
        .card {
            transition: all 0.3s ease;
            box-shadow: 0 5px 15px rgba(0,0,0,0.1);
            border-radius: 12px;
        }
        
        .card:hover {
            transform: translateY(-5px);
            box-shadow: 0 15px 30px rgba(0,0,0,0.15);
        }
        
        .highlight {
            background: linear-gradient(120deg, #f6d365 0%, #fda085 100%);
            background-repeat: no-repeat;
            background-size: 100% 40%;
            background-position: 0 88%;
        }
        
        .section-divider {
            height: 1px;
            background: linear-gradient(90deg, rgba(0,0,0,0) 0%, rgba(0,0,0,0.1) 50%, rgba(0,0,0,0) 100%);
        }
    </style>
</head>
<body class="bg-gray-50">
    <!-- Hero Section -->
    <section class="hero text-white py-20 px-4 md:px-0">
        <div class="container mx-auto max-w-6xl px-4">
            <div class="flex flex-col md:flex-row items-center">
                <div class="md:w-1/2 mb-10 md:mb-0">
                    <h1 class="text-4xl md:text-5xl font-bold mb-6 leading-tight">在Spring MVC中构建<span class="highlight">RESTful API</span></h1>
                    <p class="text-xl mb-8 opacity-90">掌握现代Web开发的核心技能，构建高效、可扩展的API服务</p>
                    <div class="flex space-x-4">
                        <a href="#getting-started" class="bg-white text-purple-700 px-6 py-3 rounded-lg font-semibold hover:bg-gray-100 transition duration-300">
                            <i class="fas fa-rocket mr-2"></i>快速开始
                        </a>
                        <a href="#best-practices" class="border-2 border-white text-white px-6 py-3 rounded-lg font-semibold hover:bg-white hover:bg-opacity-10 transition duration-300">
                            <i class="fas fa-star mr-2"></i>最佳实践
                        </a>
                    </div>
                </div>
                <div class="md:w-1/2 flex justify-center">
                    <div class="bg-white bg-opacity-20 p-8 rounded-2xl backdrop-blur-sm">
                        <img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/spring/spring-original-wordmark.svg" alt="Spring Logo" class="w-full h-auto max-h-64">
                    </div>
                </div>
            </div>
        </div>
    </section>

    <!-- Main Content -->
    <div class="container mx-auto max-w-6xl px-4 py-12">
        <!-- Introduction -->
        <section class="mb-20">
            <div class="bg-white p-8 rounded-xl shadow-lg card">
                <p class="text-lg leading-relaxed">
                    在 <span class="font-bold text-purple-600">Spring MVC</span> 中实现 <span class="font-bold text-purple-600">RESTful API</span> 是一种常见的需求，它允许你构建符合 REST（Representational State Transfer）原则的 API，这种 API 具有良好的可扩展性和可维护性。
                </p>
            </div>
        </section>

        <!-- RESTful API 的基本概念 -->
        <section id="basic-concepts" class="mb-20">
            <div class="flex items-center mb-8">
                <div class="mr-4">
                    <div class="bg-purple-100 text-purple-700 p-3 rounded-full">
                        <i class="fas fa-lightbulb text-2xl"></i>
                    </div>
                </div>
                <h2 class="text-3xl font-bold text-gray-800">RESTful API 的基本概念</h2>
            </div>
            
            <div class="bg-white p-8 rounded-xl shadow-lg card mb-8">
                <p class="text-lg mb-4">
                    <span class="font-bold text-purple-600">REST</span> 是一种架构风格，旨在通过 HTTP 协议提供资源的访问和操作。它利用了 HTTP 方法（GET、POST、PUT、DELETE）来操作资源，并通常以 JSON 或 XML 格式传输数据。
                </p>
                
                <div class="mt-6 grid md:grid-cols-4 gap-6">
                    <div class="bg-gray-50 p-4 rounded-lg border border-gray-200">
                        <div class="text-purple-600 mb-2">
                            <i class="fas fa-download text-2xl"></i>
                        </div>
                        <h3 class="font-bold mb-1">GET</h3>
                        <p class="text-gray-600 text-sm">获取资源</p>
                    </div>
                    <div class="bg-gray-50 p-4 rounded-lg border border-gray-200">
                        <div class="text-green-600 mb-2">
                            <i class="fas fa-plus-circle text-2xl"></i>
                        </div>
                        <h3 class="font-bold mb-1">POST</h3>
                        <p class="text-gray-600 text-sm">创建资源</p>
                    </div>
                    <div class="bg-gray-50 p-4 rounded-lg border border-gray-200">
                        <div class="text-blue-600 mb-2">
                            <i class="fas fa-sync-alt text-2xl"></i>
                        </div>
                        <h3 class="font-bold mb-1">PUT</h3>
                        <p class="text-gray-600 text-sm">更新资源</p>
                    </div>
                    <div class="bg-gray-50 p-4 rounded-lg border border-gray-200">
                        <div class="text-red-600 mb-2">
                            <i class="fas fa-trash-alt text-2xl"></i>
                        </div>
                        <h3 class="font-bold mb-1">DELETE</h3>
                        <p class="text-gray-600 text-sm">删除资源</p>
                    </div>
                </div>
            </div>
        </section>

        <!-- 实现 RESTful API 的步骤 -->
        <section id="getting-started" class="mb-20">
            <div class="flex items-center mb-8">
                <div class="mr-4">
                    <div class="bg-blue-100 text-blue-700 p-3 rounded-full">
                        <i class="fas fa-code text-2xl"></i>
                    </div>
                </div>
                <h2 class="text-3xl font-bold text-gray-800">实现 RESTful API 的步骤</h2>
            </div>
            
            <!-- 2.1 创建 Spring Boot 项目 -->
            <div class="mb-12">
                <div class="flex items-center mb-4">
                    <div class="w-8 h-8 bg-blue-500 text-white rounded-full flex items-center justify-center mr-3">
                        1
                    </div>
                    <h3 class="text-2xl font-semibold text-gray-800">创建 Spring Boot 项目</h3>
                </div>
                
                <div class="bg-white p-6 rounded-xl shadow-lg card mb-4">
                    <p class="mb-4">使用 Spring Boot 可以简化项目的配置和开发。创建一个 Spring Boot 项目，确保添加了 <code class="bg-gray-100 px-2 py-1 rounded">spring-boot-starter-web</code> 依赖，它包含了构建 RESTful API 所需的基础设施。</p>
                    
                    <div class="code-block p-4 rounded-lg mb-4">
                        <pre class="text-gray-100 overflow-x-auto"><code class="language-xml">
&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
&lt;/dependency&gt;
                        </code></pre>
                    </div>
                </div>
            </div>
            
            <!-- 2.2 定义资源（Entity） -->
            <div class="mb-12">
                <div class="flex items-center mb-4">
                    <div class="w-8 h-8 bg-blue-500 text-white rounded-full flex items-center justify-center mr-3">
                        2
                    </div>
                    <h3 class="text-2xl font-semibold text-gray-800">定义资源（Entity）</h3>
                </div>
                
                <div class="bg-white p-6 rounded-xl shadow-lg card mb-4">
                    <p class="mb-4">创建一个表示资源的类。例如，如果你在构建一个图书管理系统，可以定义一个 <code class="bg-gray-100 px-2 py-1 rounded">Book</code> 类：</p>
                    
                    <div class="code-block p-4 rounded-lg mb-4">
                        <pre class="text-gray-100 overflow-x-auto"><code class="language-java">
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String title;
    private String author;

    // Getters and Setters
}
                        </code></pre>
                    </div>
                </div>
            </div>
            
            <!-- 2.3 创建数据访问层（Repository） -->
            <div class="mb-12">
                <div class="flex items-center mb-4">
                    <div class="w-8 h-8 bg-blue-500 text-white rounded-full flex items-center justify-center mr-3">
                        3
                    </div>
                    <h3 class="text-2xl font-semibold text-gray-800">创建数据访问层（Repository）</h3>
                </div>
                
                <div class="bg-white p-6 rounded-xl shadow-lg card mb-4">
                    <p class="mb-4">使用 Spring Data JPA 创建一个接口来进行数据访问。这将简化对数据库的 CRUD 操作。</p>
                    
                    <div class="code-block p-4 rounded-lg mb-4">
                        <pre class="text-gray-100 overflow-x-auto"><code class="language-java">
import org.springframework.data.jpa.repository.JpaRepository;

public interface BookRepository extends JpaRepository&lt;Book, Long&gt; {
}
                        </code></pre>
                    </div>
                </div>
            </div>
            
            <!-- 2.4 创建服务层（Service） -->
            <div class="mb-12">
                <div class="flex items-center mb-4">
                    <div class="w-8 h-8 bg-blue-500 text-white rounded-full flex items-center justify-center mr-3">
                        4
                    </div>
                    <h3 class="text-2xl font-semibold text-gray-800">创建服务层（Service）</h3>
                </div>
                
                <div class="bg-white p-6 rounded-xl shadow-lg card mb-4">
                    <p class="mb-4">服务层负责业务逻辑的实现。创建一个 <code class="bg-gray-100 px-2 py-1 rounded">BookService</code> 类来处理 <code class="bg-gray-100 px-2 py-1 rounded">Book</code> 对象的业务逻辑。</p>
                    
                    <div class="code-block p-4 rounded-lg mb-4">
                        <pre class="text-gray-100 overflow-x-auto"><code class="language-java">
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;

@Service
public class BookService {
    @Autowired
    private BookRepository bookRepository;

    public List&lt;Book&gt; getAllBooks() {
        return bookRepository.findAll();
    }

    public Optional&lt;Book&gt; getBookById(Long id) {
        return bookRepository.findById(id);
    }

    public Book saveBook(Book book) {
        return bookRepository.save(book);
    }

    public void deleteBook(Long id) {
        bookRepository.deleteById(id);
    }
}
                        </code></pre>
                    </div>
                </div>
            </div>
            
            <!-- 2.5 创建控制器（Controller） -->
            <div class="mb-12">
                <div class="flex items-center mb-4">
                    <div class="w-8 h-8 bg-blue-500 text-white rounded-full flex items-center justify-center mr-3">
                        5
                    </div>
                    <h3 class="text-2xl font-semibold text-gray-800">创建控制器（Controller）</h3>
                </div>
                
                <div class="bg-white p-6 rounded-xl shadow-lg card mb-4">
                    <p class="mb-4">控制器处理 HTTP 请求，将请求映射到对应的业务逻辑，并返回响应。使用 <code class="bg-gray-100 px-2 py-1 rounded">@RestController</code> 注解表示这是一个 RESTful 控制器。</p>
                    
                    <div class="code-block p-4 rounded-lg mb-4">
                        <pre class="text-gray-100 overflow-x-auto"><code class="language-java">
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Optional;

@RestController
@RequestMapping("/api/books")
public class BookController {
    @Autowired
    private BookService bookService;

    @GetMapping
    public List&lt;Book&gt; getAllBooks() {
        return bookService.getAllBooks();
    }

    @GetMapping("/{id}")
    public ResponseEntity&lt;Book&gt; getBookById(@PathVariable Long id) {
        Optional&lt;Book&gt; book = bookService.getBookById(id);
        return book.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build());
    }

    @PostMapping
    public ResponseEntity&lt;Book&gt; createBook(@RequestBody Book book) {
        Book createdBook = bookService.saveBook(book);
        return ResponseEntity.status(HttpStatus.CREATED).body(createdBook);
    }

    @PutMapping("/{id}")
    public ResponseEntity&lt;Book&gt; updateBook(@PathVariable Long id, @RequestBody Book book) {
        if (!bookService.getBookById(id).isPresent()) {
            return ResponseEntity.notFound().build();
        }
        book.setId(id);
        Book updatedBook = bookService.saveBook(book);
        return ResponseEntity.ok(updatedBook);
    }

    @DeleteMapping("/{id}")
    public ResponseEntity&lt;Void&gt; deleteBook(@PathVariable Long id) {
        if (!bookService.getBookById(id).isPresent()) {
            return ResponseEntity.notFound().build();
        }
        bookService.deleteBook(id);
        return ResponseEntity.noContent().build();
    }
}
                        </code></pre>
                    </div>
                </div>
            </div>
        </section>

        <!-- RESTful API 的最佳实践 -->
        <section id="best-practices" class="mb-20">
            <div class="flex items-center mb-8">
                <div class="mr-4">
                    <div class="bg-green-100 text-green-700 p-3 rounded-full">
                        <i class="fas fa-medal text-2xl"></i>
                    </div>
                </div>
                <h2 class="text-3xl font-bold text-gray-800">RESTful API 的最佳实践</h2>
            </div>
            
            <div class="bg-white p-8 rounded-xl shadow-lg card">
                <!-- 3.1 使用 HTTP 方法 -->
                <div class="mb-10">
                    <h3 class="text-2xl font-semibold text-gray-800 mb-4 flex items-center">
                        <span class="w-8 h-8 bg-green-500 text-white rounded-full flex items-center justify-center mr-3 text-lg">1</span>
                        使用 HTTP 方法
                    </h3>
                    <ul class="list-disc pl-8 space-y-2">
                        <li><span class="font-bold text-green-600">GET</span>：用于获取资源。</li>
                        <li><span class="font-bold text-green-600">POST</span>：用于创建资源。</li>
                        <li><span class="font-bold text-green-600">PUT</span>：用于更新资源。</li>
                        <li><span class="font-bold text-green-600">DELETE</span>：用于删除资源。</li>
                    </ul>
                </div>
                
                <!-- 3.2 资源的 URI 设计 -->
                <div class="mb-10">
                    <h3 class="text-2xl font-semibold text-gray-800 mb-4 flex items-center">
                        <span class="w-8 h-8 bg-green-500 text-white rounded-full flex items-center justify-center mr-3 text-lg">2</span>
                        资源的 URI 设计
                    </h3>
                    <ul class="list-disc pl-8 space-y-2">
                        <li>URI 应该是名词，并且以复数形式表示资源集合，例如 <code class="bg-gray-100 px-1 py-0.5 rounded">/api/books</code>。</li>
                        <li>不要在 URI 中使用动词，动词应该通过 HTTP 方法来表示操作。</li>
                    </ul>
                </div>
                
                <!-- 3.3 状态码的使用 -->
                <div class="mb-10">
                    <h3 class="text-2xl font-semibold text-gray-800 mb-4 flex items-center">
                        <span class="w-8 h-8 bg-green-500 text-white rounded-full flex items-center justify-center mr-3 text-lg">3</span>
                        状态码的使用
                    </h3>
                    <div class="overflow-x-auto">
                        <table class="min-w-full bg-white border border-gray-200 rounded-lg overflow-hidden">
                            <thead class="bg-gray-50">
                                <tr>
                                    <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">状态码</th>
                                    <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">含义</th>
                                </tr>
                            </thead>
                            <tbody class="divide-y divide-gray-200">
                                <tr>
                                    <td class="px-6 py-4 whitespace-nowrap"><span class="px-2 py-1 bg-green-100 text-green-800 rounded">200 OK</span></td>
                                    <td class="px-6 py-4">请求成功</td>
                                </tr>
                                <tr>
                                    <td class="px-6 py-4 whitespace-nowrap"><span class="px-2 py-1 bg-green-100 text-green-800 rounded">201 Created</span></td>
                                    <td class="px-6 py-4">资源创建成功</td>
                                </tr>
                                <tr>
                                    <td class="px-6 py-4 whitespace-nowrap"><span class="px-2 py-1 bg-gray-100 text-gray-800 rounded">204 No Content</span></td>
                                    <td class="px-6 py-4">请求成功但无返回内容（如删除操作）</td>
                                </tr>
                                <tr>
                                    <td class="px-6 py-4 whitespace-nowrap"><span class="px-2 py-1 bg-yellow-100 text-yellow-800 rounded">400 Bad Request</span></td>
                                    <td class="px-6 py-4">请求无效，客户端错误</td>
                                </tr>
                                <tr>
                                    <td class="px-6 py-4 whitespace-nowrap"><span class="px-2 py-1 bg-red-100 text-red-800 rounded">404 Not Found</span></td>
                                    <td class="px-6 py-4">请求的资源未找到</td>
                                </tr>
                                <tr>
                                    <td class="px-6 py-4 whitespace-nowrap"><span class="px-2 py-1 bg-red-100 text-red-800 rounded">500 Internal Server Error</span></td>
                                    <td class="px-6 py-4">服务器内部错误</td>
                                </tr>
                            </tbody>
                        </table>
                    </div>
                </div>
                
                <!-- 3.4 错误处理 -->
                <div class="mb-10">
                    <h3 class="text-2xl font-semibold text-gray-800 mb-4 flex items-center">
                        <span class="w-8 h-8 bg-green-500 text-white rounded-full flex items-center justify-center mr-3 text-lg">4</span>
                        错误处理
                    </h3>
                    <p class="mb-4">使用异常处理机制（如 <code class="bg-gray-100 px-1 py-0.5 rounded">@ExceptionHandler</code> 或 <code class="bg-gray-100 px-1 py-0.5 rounded">@ControllerAdvice</code>）来处理全局异常。提供详细的错误信息和错误码。</p>
                    
                    <div class="code-block p-4 rounded-lg mb-4">
                        <pre class="text-gray-100 overflow-x-auto"><code class="language-java">
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public ResponseEntity&lt;String&gt; handleException(Exception ex) {
        // 记录异常日志
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ex.getMessage());
    }
}
                        </code></pre>
                    </div>
                </div>
                
                <!-- 3.5 数据格式和版本控制 -->
                <div class="mb-10">
                    <h3 class="text-2xl font-semibold text-gray-800 mb-4 flex items-center">
                        <span class="w-8 h-8 bg-green-500 text-white rounded-full flex items-center justify-center mr-3 text-lg">5</span>
                        数据格式和版本控制
                    </h3>
                    <ul class="list-disc pl-8 space-y-2">
                        <li>使用 JSON 作为数据交换格式。通过 <code class="bg-gray-100 px-1 py-0.5 rounded">@RequestBody</code> 和 <code class="bg-gray-100 px-1 py-0.5 rounded">@ResponseBody</code> 注解来处理 JSON 数据。</li>
                        <li>如果 API 版本管理很重要，可以在 URI 中包含版本号，例如 <code class="bg-gray-100 px-1 py-0.5 rounded">/api/v1/books</code>。</li>
                    </ul>
                </div>
                
                <!-- 3.6 请求验证和数据校验 -->
                <div>
                    <h3 class="text-2xl font-semibold text-gray-800 mb-4 flex items-center">
                        <span class="w-8 h-8 bg-green-500 text-white rounded-full flex items-center justify-center mr-3 text-lg">6</span>
                        请求验证和数据校验
                    </h3>
                    <p class="mb-4">使用 Java Bean Validation 注解（如 <code class="bg-gray-100 px-1 py-0.5 rounded">@NotNull</code>, <code class="bg-gray-100 px-1 py-0.5 rounded">@Size</code>）来验证请求数据。</p>
                    
                    <div class="code-block p-4 rounded-lg">
                        <pre class="text-gray-100 overflow-x-auto"><code class="language-java">
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

public class Book {
    @NotNull
    private Long id;

    @NotNull
    @Size(min = 1, max = 100)
    private String title;

    @NotNull
    private String author;

    // Getters and Setters
}
                        </code></pre>
                    </div>
                </div>
            </div>
        </section>
    </div>

    <!-- Footer -->
    <footer class="bg-gray-900 text-gray-300 py-12">
        <div class="container mx-auto max-w-6xl px-4">
            <div class="flex flex-col items-center">
                <div class="mb-4">
                    <h3 class="text-xl font-semibold text-white">技术小馆</h3>
                </div>
                <div>
                    <a href="http://www.yuque.com/jtostring" class="hover:text-white transition duration-300">
                        <i class="fas fa-link mr-2"></i>http://www.yuque.com/jtostring
                    </a>
                </div>
            </div>
        </div>
    </footer>

    <!-- Mermaid JS -->
    <script src="https://cdn.jsdelivr.net/npm/mermaid@latest/dist/mermaid.min.js"></script>
    <script>
        mermaid.initialize({
            startOnLoad: true,
            theme: 'default',
            flowchart: {
                useMaxWidth: true,
                htmlLabels: true,
                curve: 'basis'
            }
        });
    </script>
</body>
</html>